package universum.util;

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;

public class ioUtility {
    private static final ioUtility _instance = new ioUtility();
    private static final String loggingPath = "reports\\";
    private static final String errorLogFileName = loggingPath + "error.log";
    private static final String warningLogFileName = loggingPath + "warnings.log";
    private static final String infoLogFileName = loggingPath + "info.log";
    private static final String debugLogFileName = loggingPath + "debug.log";
    private static final String rawReportFileName = loggingPath + "raw\\report.log";
    private static final String cr = "\r\n";
    //private static String formattedReportFileName = loggingPath + "formatted\\report.log";
    private PrintStream debugStream = System.out, errorStream = System.out, infoStream = System.out, warningStream = System.out;
    private FileWriter debugFile, errorFile, infoFile, warningFile, reportFile;

    private enum silentModeType {
        error, warning, info, debug, report
    }

    // note that report will be written into the file in any case, you just can duplicate output to other stream
    private List<silentModeType> silentMode;

    public static ioUtility getInstance() {
        return _instance;
    }

    private ioUtility() {
        silentMode = new ArrayList<silentModeType>();
    }

    private void writelnOut(FileWriter file, PrintStream cout, String msg, silentModeType msgType) {
        if (silentMode.contains(msgType)) {
            writelnFile(file, msg);
        } else {
            writelnStream(cout, msg);
        }
    }

    private void writeOut(FileWriter file, PrintStream cout, String msg, silentModeType msgType) {
        if (silentMode.contains(msgType)) {
            writeFile(file, msg);
        } else {
            writeStream(cout, msg);
        }
    }

    private void writelnFile(FileWriter file, String msg) {
        if (file != null) { // TODO: delete this line
            try {
                file.write(msg + cr);
            } catch (IOException e) {
                writelnError(e);
            }
        }
    }

    private void writeFile(FileWriter file, String msg) {
        try {
            file.write(msg);
        } catch (IOException e) {
            writelnError(e);
        }
    }

    private void writelnStream(PrintStream cout, String msg) {
        cout.println(msg);
    }

    private void writeStream(PrintStream cout, String msg) {
        cout.print(msg);
    }

    public void writeDebug(String s) {
        /*if ( logger != null) {
            logger.println(s);
        } */
        //debugLogStream.println(String.Format("[{0}] {1}", new Date().toString()));
        writelnOut(debugFile, debugStream, s, silentModeType.debug);
    }

    public void writelnError(String s) {
        if (silentMode.contains(silentModeType.error) & errorFile != null) {
            try {
                errorFile.write(s);
            } catch (IOException e) {
                errorStream.println(s);
                e.printStackTrace(errorStream);
            }
        } else {
            errorStream.println(s);
        }
    }

    public void writelnError(Throwable e) {
        writelnError("[" + e.getLocalizedMessage() +"]");
        writelnError("[ERROR STACK]");
        String traceString = "";
        for (StackTraceElement call : e.getStackTrace()) traceString += call.toString() + "\n";
        writelnError(traceString);
        //e.printStackTrace();
    }

    public void writelnInfo(String s) {
        writelnOut(infoFile, infoStream, s, silentModeType.info);
    }

    public void writeInfo(String s) {
        writeOut(infoFile, infoStream, s, silentModeType.info);
    }

    public void writelnWarning(String s) {
        writelnOut(warningFile, warningStream, s, silentModeType.warning);
    }

    public void writelnReport(String s) {
        writelnFile(reportFile, s);
        if (!silentMode.contains(silentModeType.report)) writelnStream(warningStream, s);
    }

    public void setSilentMode(String modes) {
        if (modes.indexOf("e") != -1) {
            silentMode.add(silentModeType.error);
            errorFile = openFile(errorLogFileName);
        }
        if (modes.indexOf("d") != -1) {
            silentMode.add(silentModeType.debug);
            debugFile = openFile(debugLogFileName);
        }
        if (modes.indexOf("w") != -1) {
            silentMode.add(silentModeType.warning);
            warningFile = openFile(warningLogFileName);
        }
        if (modes.indexOf("i") != -1) {
            silentMode.add(silentModeType.info);
            infoFile = openFile(infoLogFileName);
        }
        if (modes.indexOf("r") != -1) silentMode.add(silentModeType.report);
        reportFile = openFile(rawReportFileName);
    }


    private FileWriter openFile(String fname) {
        return openFile(fname, true);
    }

    private FileWriter openFile(String fname, boolean append) {
        FileWriter result = null;
        try {
            result = new FileWriter(fname, append);
            result.write(getSessionStartDelimiter());
        } catch (IOException e) {
            writelnError("filesystem error on access to " + fname);
        }
        return result;
    }

    private void closeFile(FileWriter f) {
        if (f != null) {
            try {
                f.write(getSessionEndDelimiter());
                f.close();
            } catch (IOException e) {
                writelnError("error occured while closing file");
            }
        }
    }

    public void closeFiles() {
        closeFile(reportFile);
        closeFile(debugFile);
        closeFile(errorFile);
        closeFile(infoFile);
        closeFile(warningFile);
    }

    private String getSessionStartDelimiter() {
        return "";//String.format(cr + "session started [" + System.currentTimeMillis() + "]" + cr);
    }

    private String getSessionEndDelimiter() {
        return "";//String.format(cr + "session ended [" + System.currentTimeMillis() + "]" + cr);
    }

}
